# -*- coding: utf-8 -*-
'''
@author: 汪悟理
@file: Connect_G.py
@time: 2019/12/5 17:02
@desc:
'''
import socket
import time
from itertools import zip_longest
import os
import shlex
import subprocess
import uuid
from django.conf import settings
import paramiko

from utils.views import ReturnClass


class Sshmet():
    #测试调整，待完善
    def __init__(self):
        self.execcmd_out = ''   #执行结果
        self.execute_result = False   # -1失败 1成功 2警告
        self.percents = 0
        self.ip = None
        self.username = None
        self.password = None
        self.port = None
        self.timeout = 30
        self.execcmd_error = ''

    def set_info(self, host_info_dict):
        self.ip = host_info_dict["host_ip"]
        self.username = host_info_dict["resource_user_name"]
        self.password = host_info_dict["resource_user_pass"]
        self.port = host_info_dict["host_connection_port"]
        self.timeout = host_info_dict.get("timeout") and host_info_dict["timeout"] or self.timeout

    def connect(self):
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(self.ip, self.port, self.username, self.password, timeout=int(self.timeout), banner_timeout=10)
        return self.ssh

    def execcmd(self, cmd):
        """/
        执行单条命令,等待完成输出
        :param cmd:
        :return:
        """
        # env = 'source .bash_profile;source /etc/profile;export LANG=en_US.UTF-8;'
        # stdin, stdout, stderr = self.ssh.exec_command('%s%s' % (env, cmd))
        stdin, stdout, stderr = self.ssh.exec_command(cmd)
        stdout = str(stdout.read(),'utf-8')
        stderr = str(stderr.read(),'utf-8')
        #result = ''.join(stdout.read() + stderr.read())
        result = (stdout + stderr).strip()
        if not stderr == '':
            self.execcmd_error = stderr
            return ReturnClass.return_bust(result)
        return ReturnClass.return_succeed(result)

    def runcrealtime(self, command, callback=None):
        """
        实时执行命令
        :param command:  命令
        :param callback: 回调函数 必填：{readline_text:XXX}
        :return:
        """
        #回调函数默认收集为class.execcmd_out变量
        if not callback:
            callback = self.execcmdout

        stdin, stdout, stderr = self.ssh.exec_command(
            command, bufsize=1
        )

        stdout_iter = iter(stdout.readline, '')
        stderr_iter = iter(stderr.readline, '')

        err_status = 0
        out_status = 0
        #标准输出
        for line in stdout_iter:
            callback(line.strip())
            out_status = 1
        #标准输出结束后收集错误输出
        for line in stderr_iter:
            callback(line.strip())
            err_status = 1

        if out_status == 1 and err_status == 1:
            self.execute_result = 2
        elif err_status == 1:
            self.execute_result = -1
        else:
            self.execute_result = 1

        # test = zip_longest(stdout_iter, stderr_iter)
        # for out, err in zip(stdout_iter, stderr_iter):
        #     if out: callback(out.strip())
        #     if err: callback(err.strip())

        return stdin, stdout, stderr

    def execcmdout(self, text):
        self.execcmd_out += str(text)
        print(str(text))


    def check_status(self, check_code, check_url, location="target"):
        if check_code == 'http':
            if location == "target":
                exec_result = self.execcmd("curl -I -m 10 -o /dev/null -s -w %%{http_code} %s" % check_url)
                if ReturnClass.if_ok(exec_result):
                    exec_data = ReturnClass.get_value(exec_result)
                    if exec_data in ("200", "304"):
                        return ReturnClass.return_succeed("服务启动成功")
                return ReturnClass.return_bust("服务启动失败")
            else:
                return ReturnClass.return_bust("不支持的检测方法")
        elif check_code == 'process':
            return self.execcmd()
        elif check_code == 'port':
            return self.execcmd("")
        else:
            return ReturnClass.return_bust("不支持的检测方法")


    def close(self):
        self.ssh.close()


class SCPMet(Sshmet):

    def cp_connect(self):
        self.ssh = self.connect()
        self.ssht = paramiko.Transport((self.ip, self.port))
        self.ssht.connect(username=self.username, password=self.password)
        self.sftp = paramiko.SFTPClient.from_transport(self.ssht)
        return self.sftp

    def get_file(self, src_file, des_file):
        self.sftp.get(src_file, des_file)
        return des_file

    def progress_bar(self, transferred, toBeTransferred, suffix=''):
        bar_len = 100
        filled_len = int(round(bar_len * transferred / float(toBeTransferred)))
        percents = round(100.0 * transferred / float(toBeTransferred), 1)
        self.percents = percents
        return True

    def put_file(self, src_file, des_file):
        self.sftp.put(src_file, des_file, callback=self.progress_bar)
        return des_file

    def text_save_file_put_file(self, text, des_file):
        """
        保存文字到本地，并上传到服务器存为文件
        @param text:
        @param des_file:
        @return:
        """
        local_path = os.path.join(settings.APP_CONF['TMPPATHGIT'], str(uuid.uuid1()))
        file = open(local_path, 'w')
        file.write(text)
        file.close()
        return self.put_file(local_path, des_file)

    def put_file_exec(self, src_file, des_file, param, type='sh',nohup=True, callback=None):
        """
        上传文件并执行
        :param src_file: 源文件
        :param des_file: 目标文件
        :param param: 脚本参数
        :param type:  python or sh ?
        :return:
        """
        src_file = src_file.replace('\\','/')
        des_file = des_file.replace('\\','/')

        self.put_file(src_file, des_file)

        file_name = str(uuid.uuid1())

        if nohup:
            cmd = 'nohup %s %s %s > %s 2>&1 &' % (type, des_file, param, file_name)
            cmd_result = self.execcmd(cmd)
        else:
            cmd = '%s %s %s' % (type, des_file, param)
            cmd_result = self.runcrealtime(cmd, callback)

        return cmd_result

    def list_dir(self, dir_name):
        return self.sftp.listdir(dir_name)

    def __get_all_files_in_local_dir(self, local_dir):
        all_files = list()
        files = os.listdir(local_dir)
        for x in files:
            filename = os.path.join(local_dir, x)
            if os.path.isdir(x):
                all_files.extend(self.__get_all_files_in_local_dir(filename))
            else:
                all_files.append(filename)
        return all_files


    def open_file_text(self, path_file, nu):
        cmd = "tail -n %s %s" %(nu, path_file)
        cmd_result = self.execcmd(cmd)
        return cmd_result


    def close(self):
        self.ssh.close()
        try:
            self.sftp.close()
        except:
            pass



class Ip_c():
    def __init__(self):
        self.succeed = True

    def string_formatting(self, ipstr):
        """
        解析连续IP字符，返回数组
        :param ipstr: 192.168.0.1-192,192.168.0.199
        :return:
        """
        ip_addrs = []
        for ipstr_one in ipstr.split(','):
            if ipstr_one.find('-') != -1:
                basket_dict_one_separation = ipstr_one.split('-')
                if self.if_ipaddr(basket_dict_one_separation[0]) and len(str(basket_dict_one_separation[1])) < 4:
                    start_number = basket_dict_one_separation[0].split(".")[-1]
                    start_number_s = basket_dict_one_separation[0].rstrip(start_number)
                    for number_one in range(int(start_number) , int(basket_dict_one_separation[1]) + 1):
                        ip_addrs.append(start_number_s + str(number_one))
            else:
                if self.if_ipaddr(ipstr_one):
                    ip_addrs.append(ipstr_one)
        return ip_addrs

    def if_ipaddr(self, ip):
        """
        判断字符串是否为IP
        :param ipstr: 192.168.0.1
        :return:
        """
        # ip_split = ip.split('.')
        # if not len(ip_split) == 4:
        #     self.succeed = False
        #     return False
        # for ip_split_one in ip_split:
        #     if not 0 < int(ip_split_one) < 255:
        #         self.succeed = False
        #         return False
        # return ip

        try:
            socket.inet_pton(socket.AF_INET, ip)
            return ip
        except:
            try:
                socket.inet_pton(socket.AF_INET6, ip)
                return ip
            except:
                return False


class LinuxShell(object):
    # 通过IP, 用户名，密码，超时时间初始化一个远程Linux主机
    def __init__(self, ip, username, password, timeout=30):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout
        # transport和chanel
        self.t = ''
        self.chan = ''
        # 链接失败的重试次数
        self.try_times = 3

    # 调用该方法连接远程主机
    def connect(self):
        while True:
            # 连接过程中可能会抛出异常，比如网络不通、链接超时
            try:
                # self.t = paramiko.Transport(sock=(self.ip, 22))
                # self.t.connect(username=self.username, password=self.password)
                # self.chan = self.t.open_session()
                # self.chan.settimeout(self.timeout)
                # self.chan.get_pty()
                # self.chan.invoke_shell()
                # #self.chan.invoke_shell(term='xterm')
                # #self.chan.setblocking(0)
                # # 如果没有抛出异常说明连接成功，直接返回
                # print u'连接%s成功' % self.ip
                # # 接收到的网络数据解码为str
                # return self.chan.recv(65535).decode('utf-8')

                self.client = paramiko.SSHClient()
                self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                self.client.connect(hostname=self.ip, username=self.username, password=self.password)
                self.chan = self.client.invoke_shell(term='xterm')
                self.chan.settimeout(9000)
                return self.chan.recv(4096)


            # 这里不对可能的异常如socket.error, socket.timeout细化，直接一网打尽
            except Exception as e:
                if self.try_times != 0:
                    print (u'连接%s失败，进行重试(%s)' % (self.ip,e))
                    self.try_times -= 1
                else:
                    print (u'重试3次失败，结束程序')
                    exit(1)

    # 断开连接
    def close(self):
        self.chan.close()
        self.t.close()

    # 发送要执行的命令
    def send(self, cmd):
        cmd += '\r'
        # 通过命令执行提示符来判断命令是否执行完成
        # p = re.compile(r':~ #')

        result = ''
        # 发送要执行的命令
        self.chan.send(cmd)
        # 回显很长的命令可能执行较久，通过循环分批次取回回显
        while True:
            time.sleep(0.5)
            ret = self.chan.recv(65535)
            # ret = ret.decode('utf-8')
            # result += ret
            # if p.search(ret):
            # print result
            # return result
            return ret